﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml.Linq;
using System.ComponentModel;
using Noris.Tools.FrxEditor.Data;
using Noris.Tools.FrxEditor.Editor;
using Noris.Tools.FrxEditor.Components;
using Noris.Tools.FrxEditor.ComponentsLayer;

namespace Noris.Tools.FrxEditor.ReportObjects
{
    #region class ReportGraph : abstraktní bázová třída každého grafu
    /// <summary>
    /// ReportGraph : abstraktní bázová třída každého grafu.
    /// Deklaruje abstraktní metodu GetGraphPieces().
    /// Přepisuje virtuální metodu EditorDrawContent(), zajišťuje vykreslení grafu na základě grafických dat vrácených metodou GetGraphPieces().
    /// </summary>
    public abstract class ReportGraph : ReportObject
    {
        #region Abstract
        /// <summary>
        /// Metoda má za úkol vrátit grafické střípky, z nich bude vykreslen graf v době editace.
        /// Na bázové třídě ReportGraph je abstraktní.
        /// </summary>
        /// <param name="currentVisualBound"></param>
        /// <returns></returns>
        protected abstract IEnumerable<GraphDrawPiece> EditorGetGraphPieces(RectangleF currentVisualBound);
        #endregion
        #region Vykreslení v editoru
        /// <summary>
        /// Voláno v době editace.
        /// Vykreslení obsahu.
        /// </summary>
        /// <param name="args"></param>
        protected override void EditorPaintContent(ReportObjectDrawArgs args)
        {
            IEnumerable<GraphDrawPiece> pieces = this.EditorGetGraphPieces(args.CurrentVisualBound);
            foreach (GraphDrawPiece piece in pieces)
                piece.EditorPaint(args);
        }
        /// <summary>
        /// Implicitní jméno objektu
        /// </summary>
        protected override string ImplicitName
        {
            get
            {
                string name = this.GetType().Name;           // Jméno třídy bez namespace
                name = name.Replace("ReportGraph", "Graph");
                return name;
            }
        }
        #endregion
    }
    #endregion
    #region class ReportGraph3D : abstraktní bázová třída každého grafu, který pracuje se 3D zobrazením
    /// <summary>
    /// ReportGraph3D : abstraktní bázová třída každého grafu, který pracuje se 3D zobrazením
    /// </summary>
    public abstract class ReportGraph3D : ReportGraph
    {
        #region Konstrukce
        public ReportGraph3D()
        {
            this.BackColor = Color.GhostWhite;
            this.SortType = GraphItemSortType.SortByPosition;
            this.TargetPoint = new PointF(0.667f, 0.750f);
            this.DepthRatio = 0.35f;
        }
        #endregion
        #region Vizuální a 3D vlastnosti grafu
        /// <summary>
        /// Barva pozadí
        /// </summary>
        [Category("Appearance")]
        [DisplayName("Barva pozadí")]
        [Description("Barva pozadí grafu")]
        public Color BackColor { get; set; }
        /// <summary>
        /// Třídění položek do grafu
        /// </summary>
        [Category("Appearance")]
        [DisplayName("Třídění")]
        [Description("Třídění položek do grafu")]
        public GraphItemSortType SortType { get; set; }
        /// <summary>
        /// Cílový bod perspektivy = úběžník. Nastavuje se interaktivně (má svůj segment), nikoli v PropertyGridu.
        /// Bod se pohybuje v rozsahu 0 až 1 včetně, přičemž 0 je vlevo nahoře a 1 je vpravo dole.
        /// </summary>
        [Browsable(false)]
        public PointF TargetPoint { get; set; }
        /// <summary>
        /// Hloubka perspektivy. Nastavuje se interaktivně (má svůj segment), nikoli v PropertyGridu.
        /// Ratio se pohybuje od 0 do 0.95, přičemž 0 je nekonečno a 0.95 je úhel pohledu cca 45%
        /// </summary>
        [Browsable(false)]
        public float DepthRatio
        {
            get { return this._DepthRatio; }
            set
            {
                if (value < 0f) this._DepthRatio = 0f;
                else if (value > 0.95f) this._DepthRatio = 0.95f;
                else this._DepthRatio = value;
            }
        }
        private float _DepthRatio;
        /// <summary>
        /// Fyzický bod TargetPointu v aktuálních vizuálních souřadnicích.
        /// Používá se pro výpočty DepthRatio při interaktivním posouvání bodu DepthRatio
        /// </summary>
        protected PointF VisualTargetPoint { get; set; }
        /// <summary>
        /// Bod, na němž začíná linka DepthRatio = levý dolní roh kreslícího Rectangle, v aktuálních vizuálních souřadnicích.
        /// Používá se pro výpočty DepthRatio při interaktivním posouvání bodu DepthRatio
        /// </summary>
        protected PointF VisualDepthOriginPoint { get; set; }
        #endregion
        #region Overrides pro editor: příprava 3D segmentů, obsluha UserDefined 3D segmentů (interaktivita, vykreslování)
        /// <summary>
        /// Příprava interaktivních segmentů v režimu Select
        /// </summary>
        /// <param name="args"></param>
        protected override void PrepareInteractiveSegmentsSelect(ReportObjectPrepareSegmentsArgs args)
        {
            RectangleF visualBound = args.CurrentVisualBound;
            float x = visualBound.X;
            float w = visualBound.Width;
            float y = visualBound.Y;
            float h = visualBound.Height;

            float xt = this.TargetPoint.X * w;
            float yt = this.TargetPoint.Y * h;
            if (args.IsWithMouse)
            {
                // DepthPoint nejdříve, bude na ose Z "pod" TargetPointem:
                this.VisualDepthOriginPoint = new PointF(x, y + h);
                float depth = this.DepthRatio;
                PointF depthPoint = new PointF(GetDepthDim(x, xt, depth), GetDepthDim(y + h, yt - h, depth));
                args.PrepareInteractiveSegmentsOne(this, UserSegmentType.DepthPoint, true,
                    new SheetF(depthPoint.GetRectangleFFromCenter(8f)),
                    depthPoint,
                    depthPoint.GetRectangleFFromCenter(6f),
                    SysCursorType.SizeAll, SysCursorType.SizeAll, SysCursorType.NoMoveAuto,
                    EditorItemSegmentGripType.UserDefined,
                    true, true, false, true, false,
                    EditMouseChangeType.OnChange);

                // TargetPoint poté, bude na ose Z "nad" DepthPointem = pokud budou na sobě, tak se bude tahat za TargetPoint:
                PointF targetPoint = new PointF(x + xt, y + yt);
                this.VisualTargetPoint = targetPoint;
                args.PrepareInteractiveSegmentsOne(this, UserSegmentType.TargetPoint, true,
                    new SheetF(targetPoint.GetRectangleFFromCenter(8f)),
                    targetPoint,
                    targetPoint.GetRectangleFFromCenter(6f),
                    SysCursorType.SizeAll, SysCursorType.SizeAll, SysCursorType.NoMoveAuto,
                    EditorItemSegmentGripType.UserDefined,
                    true, true, false, true, false,
                    EditMouseChangeType.OnChange);
            }
        }
        /// <summary>
        /// Vrátí souřadnici bodu Depth (na ose X nebo Y, podle zadaných dat begin a size) pro danou hodnotu depth (ratio)
        /// </summary>
        /// <param name="begin"></param>
        /// <param name="size"></param>
        /// <param name="depth"></param>
        /// <returns></returns>
        protected float GetDepthDim(float begin, float size, float depth)
        {
            if (depth == 0f || size == 0f) return begin;
            return begin + depth * size;
        }
        /// <summary>
        /// Vykreslí UserDefined segmenty
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        protected override void EditorDrawSegmentUser(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            if (segment != null && segment.SegmentType is UserSegmentType)
            {
                UserSegmentType type = (UserSegmentType)segment.SegmentType;
                switch (type)
                {
                    case UserSegmentType.TargetPoint:
                        this.EditorDrawSegmentUserTargetPoint(args, segment);
                        break;
                    case UserSegmentType.DepthPoint:
                        this.EditorDrawSegmentUserDepthPoint(args, segment);
                        break;
                }
            }
        }
        /// <summary>
        /// Vykreslí segment typu UserSegmentType.TargetPoint
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        protected virtual void EditorDrawSegmentUserTargetPoint(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            using (Brush brush = this.EditorGetBrushForSegment(args, segment))
            using (Pen pen = this.EditorGetPenForTargetPoint(args, segment))
            {
                var state = args.Graphics.Save();
                args.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                float x1 = args.CurrentVisualBound.X;
                float x2 = args.CurrentVisualBound.Right;
                float y1 = args.CurrentVisualBound.Y;
                float y2 = args.CurrentVisualBound.Bottom;

                PointF targetPoint = segment.SegmentPoint;

                if (pen != null)
                {
                    args.Graphics.DrawLine(pen, new PointF(x1, y1), targetPoint);
                    args.Graphics.DrawLine(pen, new PointF(x2, y1), targetPoint);
                    args.Graphics.DrawLine(pen, new PointF(x1, y2), targetPoint);
                    args.Graphics.DrawLine(pen, new PointF(x2, y2), targetPoint);
                }

                if (brush != null)
                {
                    args.Graphics.FillEllipse(brush, segment.SegmentVisualBound);
                }
                args.Graphics.DrawLine(Pens.Black, targetPoint.X - 4f, targetPoint.Y - 4f, targetPoint.X + 4f, targetPoint.Y + 4f);
                args.Graphics.DrawLine(Pens.Black, targetPoint.X - 4f, targetPoint.Y + 4f, targetPoint.X + 4f, targetPoint.Y - 4f);

                args.Graphics.Restore(state);
            }
        }
        /// <summary>
        /// Vrátí pero pro kreslení čar k úběžníku perspektivy (TargetPoint)
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        /// <returns></returns>
        protected virtual Pen EditorGetPenForTargetPoint(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            Pen pen = new Pen(Color.LightGray);
            pen.DashStyle = DashStyle.Dot;
            return pen;
        }
        /// <summary>
        /// Vykreslí segment typu UserSegmentType.DepthPoint
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        private void EditorDrawSegmentUserDepthPoint(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            using (Brush brush = this.EditorGetBrushForSegment(args, segment))
            {
                var state = args.Graphics.Save();
                args.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                PointF depthPoint = segment.SegmentPoint;
                args.Graphics.FillEllipse(brush, segment.SegmentVisualBound);
                args.Graphics.Restore(state);
            }
        }
        /// <summary>
        /// Dovoluje objektu řešit jakoukoli interaktivitu mezi myší a objektem
        /// </summary>
        /// <param name="args"></param>
        protected override void EditorSegmentInteractivity(ReportObjectInteractiveSegmentsArgs args)
        {
            if (args.MouseChange == EditMouseChangeType.LeftDragMove && args.ActiveSegment != null && args.ActiveSegment.GripType == EditorItemSegmentGripType.UserDefined && args.ActiveSegment.SegmentType is UserSegmentType)
                this.EditorDragUserSegment(args);
        }
        /// <summary>
        /// Přetahování UserDefined segmentů
        /// </summary>
        /// <param name="args"></param>
        protected virtual void EditorDragUserSegment(ReportObjectInteractiveSegmentsArgs args)
        {
            UserSegmentType type = (UserSegmentType)args.ActiveSegment.SegmentType;
            switch (type)
            {
                case UserSegmentType.TargetPoint:
                    this.EditorDragUserSegmentTargetPoint(args);
                    break;
                case UserSegmentType.DepthPoint:
                    this.EditorDragUserSegmentDepthPoint(args);
                    break;
            }
        }
        /// <summary>
        /// Přetahování UserDefined segmentu typu TargetPoint
        /// </summary>
        /// <param name="args"></param>
        private void EditorDragUserSegmentTargetPoint(ReportObjectInteractiveSegmentsArgs args)
        {
            PointF targetPoint = args.CurrentVisualBound.GetPointFRelative(args.MousePointF);
            targetPoint.X = (targetPoint.X < 0f ? 0f : targetPoint.X > 1f ? 1f : targetPoint.X);
            targetPoint.Y = (targetPoint.Y < 0f ? 0f : targetPoint.Y > 1f ? 1f : targetPoint.Y);
            this.TargetPoint = targetPoint;
            args.Handled = true;
            args.ReDrawInteractive = true;
        }
        /// <summary>
        /// Přetahování UserDefined segmentu typu DepthPoint
        /// </summary>
        /// <param name="args"></param>
        private void EditorDragUserSegmentDepthPoint(ReportObjectInteractiveSegmentsArgs args)
        {
            // Segment zobrazující DepthPoint se pohybuje na lince od levého dolního rohu (this.VisualDepthPoint) po bod TargetPoint (this.VisualDepthOriginPoint).
            // Zde určíme vzdálenost bodu myši od dané úsečky, a uložíme ji do this.DepthRatio:
            PointF b = this.VisualDepthOriginPoint;
            PointF e = this.VisualTargetPoint;
            PointF m = args.MousePointF;

            float w = e.X - b.X;            // Šířka úhlopříčky, po níž se bod DepthPoint přetahuje
            float h = b.Y - e.Y;            // Výška...
            float d = w + h;                // Prostý součet nahrazuje délku přepony
            if (d == 0f)
            {   // Pokud není prostor pro pohyb (někdo dal úběžník perspektivy TargetPoint doleva dolů), není kde přetahovat DepthPoint:
                args.Handled = true;
                return;
            }

            float r = 0f;
            if (w != 0f)                    // Pokud je šířka nenulová, tak nás zajímá posun v ose X:
                r += Math.Abs(w / d) * (m.X - b.X) / w;   // Posun myši (m.X) proti bodu počátku (b.X) v poměru k šířce posunu: přispívá k výsledku v poměru (podílu šířky na celkové délce (w / d))
            if (h != 0f)
                r += Math.Abs(h / d) * (b.Y - m.Y) / h;

            this.DepthRatio = r;            // Zarovnání do správných mezí provede set{}

            args.Handled = true;
            args.ReDrawInteractive = true;
        }
        /// <summary>
        /// Typy UserDefined segmentů
        /// </summary>
        protected enum UserSegmentType
        {
            None = 0,
            TargetPoint,
            DepthPoint,
            TiltPoint
        }
        #endregion
        #region Podpora pro tvorbu Pieces
        /// <summary>
        /// Vytvoří a vrátí objekt View3D pro aktuální parametry 3D a pro dané 2D souřadnice grafu
        /// </summary>
        /// <param name="bounds"></param>
        /// <returns></returns>
        public virtual View3D CreateView3D(RectangleF bounds)
        {
            return new View3D(bounds, this.TargetPoint, this.DepthRatio);
        }
        /// <summary>
        /// Vytvoří a vrátí objekt Block3D pro aktuální parametry 3D a pro dané 2D souřadnice grafu
        /// </summary>
        /// <param name="bounds"></param>
        /// <returns></returns>
        public virtual Block3D CreateGraphBlock3D(RectangleF bounds)
        {
            float depth = 0.15f * (bounds.Width + bounds.Height);
            return new Block3D(new Point3D(bounds.X, bounds.Y, 0f), new Size3D(bounds.Width, bounds.Height, depth));
        }

        #endregion
    }
    #endregion
    #region class GraphData : Abstraktní datová třída GraphData, určená pro evidenci dat grafu a k tvorbě vizuální reprezentace. Nikoli editace grafu a jeho vykreslování.
    /// <summary>
    /// GraphData : Abstraktní datová třída GraphData, určená pro evidenci dat grafu a k tvorbě vizuální reprezentace. Nikoli editace grafu a jeho vykreslování.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class GraphData<T>
    {
        public GraphData()
        {
            this.GraphItemList = new List<GraphDataItem<T>>();
        }
        /// <summary>
        /// Seznam položek grafu
        /// </summary>
        public List<GraphDataItem<T>> GraphItemList { get; set; }
        /// <summary>
        /// Počet řad grafu
        /// </summary>
        public int ItemSeriesCount { get { return this.ItemSeries.Count(); } }
        /// <summary>
        /// Indexy řad grafu
        /// </summary>
        public IEnumerable<int> ItemSeries { get { return this.GraphItemList.ConvertAll(i => i.Series).Distinct(); } }
        public decimal? PositionMin { get { return  this.GraphItemList.Min(i => i.Position); } }
        public decimal? PositionMax { get { return this.GraphItemList.Max(i => i.Position); } }
        public decimal? ValueMin { get { return this.GraphItemList.Min(i => i.Value); } }
        public decimal? ValueMax { get { return this.GraphItemList.Max(i => i.Value); } }
        public decimal? ValueSum { get { return this.GraphItemList.Sum(i => i.Value); } }

        #region Předpis abstraktních metod pro potomky
        /// <summary>
        /// Klíčová metoda každého grafu, kdy graf transformuje svoje logická data do vizuálních střípků, které aktuální graf zobrazují.
        /// Bázová třída grafu deklaruje abstraktní metodu, práci musí odvést potomek.
        /// </summary>
        public abstract IEnumerable<GraphDrawPiece> GetGraphPieces(RectangleF bounds);
        /// <summary>
        /// Naplní do this data pro zobrazení v editoru.
        /// Bázová třída grafu deklaruje abstraktní metodu, práci musí odvést potomek.
        /// </summary>
        public abstract void FillValuesForEditor();
        #endregion
        #region Podpora pro potomstvo - metody pro tvorbu Pieces
        /// <summary>
        /// Vrátí List obsahující zdejší data ze seznamu GraphItemList, setříděný daným třídičem.
        /// Pokud je seznam prázdný, vrací null.
        /// </summary>
        /// <param name="sortType"></param>
        /// <returns></returns>
        protected virtual List<GraphDataItem<T>> GetSortedItems(GraphItemSortType sortType)
        {
            List<GraphDataItem<T>> list = new List<GraphDataItem<T>>(this.GraphItemList);
            int itemCount = list.Count;
            if (itemCount == 0) return null;
            if (itemCount > 1)
                this.SortItemList(list, sortType);
            return list;
        }
        /// <summary>
        /// Setřídí daný list daným třídičem
        /// </summary>
        /// <param name="list"></param>
        /// <param name="sortType"></param>
        private void SortItemList(List<GraphDataItem<T>> list, GraphItemSortType sortType)
        {
            switch (sortType)
            {
                case GraphItemSortType.SortByPosition:
                    list.Sort(GraphDataItem<T>.CompareByPosition);
                    break;
                case GraphItemSortType.SortByValueAsc:
                    list.Sort(GraphDataItem<T>.CompareByValueAsc);
                    break;
                case GraphItemSortType.SortByValueDesc:
                    list.Sort(GraphDataItem<T>.CompareByValueDesc);
                    break;
            }
        }
        /// <summary>
        /// Do výsledného seznamu (result) přidá Pieces za vlastní prostor grafu
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="block"></param>
        /// <param name="view3D"></param>
        /// <param name="backColor"></param>
        /// <param name="result"></param>
        protected virtual void AddGraphPiecesBack(RectangleF bounds, Block3D block, View3D view3D, Color backColor, List<GraphDrawPiece> result)
        {
            Color lineColor = backColor.ChangeColor(-0.5f);
            AddGraphPiecesBack(bounds, view3D, block.Sides[Block3DSide.Frontal], Color.Empty, 0f, backColor, 0f, result);
            AddGraphPiecesBack(bounds, view3D, block.Sides[Block3DSide.Top], lineColor, 1f, backColor, -0.25f, result);
            AddGraphPiecesBack(bounds, view3D, block.Sides[Block3DSide.Left], lineColor, 1f, backColor, -0.10f, result);
            AddGraphPiecesBack(bounds, view3D, block.Sides[Block3DSide.Right], lineColor, 1f, backColor, 0.25f, result);
            AddGraphPiecesBack(bounds, view3D, block.Sides[Block3DSide.Bottom], lineColor, 1f, backColor, -0.50f, result);
        }
        /// <summary>
        /// Do výsledného seznamu (result) přidá jeden daný Piece za vlastní prostor grafu
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="view3D"></param>
        /// <param name="polygon"></param>
        /// <param name="lineColor"></param>
        /// <param name="lineWidth"></param>
        /// <param name="fillColor"></param>
        /// <param name="fillColorChange"></param>
        /// <param name="result"></param>
        protected virtual void AddGraphPiecesBack(RectangleF bounds, View3D view3D, Polygon3D polygon, Color lineColor, float lineWidth, Color fillColor, float fillColorChange, List<GraphDrawPiece> result)
        {
            GraphDrawPiece piece = new GraphDrawPiece()
            {
                PieceType = GraphDrawPieceType.Path,
                Bounds = bounds,
                FillColor = fillColor.ChangeColor(fillColorChange),
                LineColor = lineColor,
                LineWidth = lineWidth,
                Path = view3D.TranslateToPath(polygon)
            };
            result.Add(piece);
        }
        #endregion
    }
    /// <summary>
    /// Data popisující jednu každou položku dat (bod grafu, jeden prvek)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GraphDataItem<T>
    {
        /// <summary>
        /// Pozice položky grafu.
        /// Po graf X-Y hodnota X.
        /// </summary>
        public decimal Position { get; set; }
        /// <summary>
        /// Sada dat grafu (řada).
        /// Po graf X-Y jde o číslo linky.
        /// Pro jiné grafy jde o index datové řady.
        /// </summary>
        public int Series { get; set; }
        /// <summary>
        /// Hodnota položky grafu.
        /// Po graf X-Y hodnota Y.
        /// Pro jiné grafy pořadové číslo položky.
        /// </summary>
        public decimal Value { get; set; }
        /// <summary>
        /// Barva prvku grafu
        /// </summary>
        public Color GraphColor { get; set; }
        /// <summary>
        /// Text u prvku grafu
        /// </summary>
        public string Caption { get; set; }
        /// <summary>
        /// Rozšiřující data o položce grafu, závisí na konkrétním typu grafu jak bude hodnoty položky interpretovat
        /// </summary>
        public T GraphItemInfo { get; set; }
        public static int CompareByPosition(GraphDataItem<T> a, GraphDataItem<T> b)
        {
            return a.Position.CompareTo(b.Position);
        }
        public static int CompareByValueAsc(GraphDataItem<T> a, GraphDataItem<T> b)
        {
            return a.Value.CompareTo(b.Value);
        }
        public static int CompareByValueDesc(GraphDataItem<T> a, GraphDataItem<T> b)
        {
            return b.Value.CompareTo(a.Value);
        }
    }
    /// <summary>
    /// Třída, která popisuje jednotlivý střípek grafické informace.
    /// Z řady těchto střípků se skládá graf.
    /// Třídu lze dědit a přepisovat metody, tak lze dále rozšiřovat variabilitu grafů.
    /// Typicky se přidá další property která nese další data, a přepíše se některá metoda (buď kreslící, nebo tvorba Brush / Pen).
    /// Jádro grafu přitom stále standardně zpracovává sadu Pieces...
    /// </summary>
    public class GraphDrawPiece
    {
        /// <summary>
        /// Druh střípku (Text, RectangleF, GraphicsPath)
        /// </summary>
        public GraphDrawPieceType PieceType { get; set; }
        /// <summary>
        /// Souřadnice prostoru.
        /// Pro PieceType = Text jde o prostor textu.
        /// Pro PieceType = Area jde o souřadnice prostoru (Rectangle).
        /// Pro PieceType = Path jde o vnější ohraničující prostor, pro který se budou vytvářet nástroje (Brush).
        /// </summary>
        public RectangleF Bounds { get; set; }
        /// <summary>
        /// Grafický tvar (polygony, elipsy, atd)
        /// </summary>
        public GraphicsPath Path { get; set; }
        public string Text { get; set; }
        public ContentAlignment TextAlignment { get; set; }
        public Color FillColor { get; set; }
        public Color LineColor { get; set; }
        public float LineWidth { get; set; }
        #region Vykreslování, podpora pro něj
        /// <summary>
        /// Vykreslí tento střípek v prostředí editoru
        /// </summary>
        /// <param name="args"></param>
        public virtual void EditorPaint(ReportObjectDrawArgs args)
        {
            switch (this.PieceType)
            {
                case GraphDrawPieceType.Area:
                    this.EditorPaintArea(args);
                    break;
                case GraphDrawPieceType.Text:
                    this.EditorPaintText(args);
                    break;
                case GraphDrawPieceType.Path:
                    this.EditorPaintPath(args);
                    break;
            }
        }
        /// <summary>
        /// Vykreslí Area
        /// </summary>
        /// <param name="args"></param>
        /// <param name="piece"></param>
        public virtual void EditorPaintArea(ReportObjectDrawArgs args)
        {
            using (Brush brush = this.CreateWinFormBrush())
            {
                if (brush != null)
                    args.Graphics.FillRectangle(brush, this.Bounds);
            }
            using (Pen pen = this.CreateWinFormPen())
            {
                if (pen != null)
                    args.Graphics.DrawRectangle(pen, Rectangle.Round(this.Bounds));
            }
        }
        /// <summary>
        /// Vykreslí Text
        /// </summary>
        /// <param name="args"></param>
        /// <param name="piece"></param>
        public virtual void EditorPaintText(ReportObjectDrawArgs args)
        {

        }
        /// <summary>
        /// Vykreslí GraphicsPath
        /// </summary>
        /// <param name="args"></param>
        public virtual void EditorPaintPath(ReportObjectDrawArgs args)
        {
            var state = args.Graphics.Save();
            args.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            using (Brush brush = this.CreateWinFormBrush())
            {
                if (brush != null)
                    args.Graphics.FillPath(brush, this.Path);
            }
            using (Pen pen = this.CreateWinFormPen())
            {
                if (pen != null)
                    args.Graphics.DrawPath(pen, this.Path);
            }
            args.Graphics.Restore(state);
        }
        /// <summary>
        /// Vrátí výplň pro kreslení daného kousku, v prostředí editoru
        /// </summary>
        /// <returns></returns>
        public virtual Brush CreateWinFormBrush()
        {
            if (!IsColorVisible(this.FillColor))
                return null;
            return new SolidBrush(this.FillColor);
        }
        /// <summary>
        /// Vrátí pero pro kreslení daného kousku, v prostředí editoru
        /// </summary>
        /// <returns></returns>
        public virtual Pen CreateWinFormPen()
        {
            if (this.LineWidth <= 0f || !IsColorVisible(this.LineColor))
                return null;
            return new Pen(this.LineColor, this.LineWidth);
        }
        /// <summary>
        /// Vrátí true, pokud daná barva je viditelná
        /// </summary>
        /// <param name="color"></param>
        /// <returns></returns>
        protected bool IsColorVisible(Color color)
        {
            return (!color.IsEmpty || color.A > 0);
        }
        #endregion
    }
    /// <summary>
    /// Typ grafického střípku
    /// </summary>
    public enum GraphDrawPieceType
    {
        /// <summary>
        /// Neurčeno
        /// </summary>
        None,
        /// <summary>
        /// Prostá plocha (RectangleF)
        /// </summary>
        Area,
        /// <summary>
        /// Text
        /// </summary>
        Text,
        /// <summary>
        /// GraphicsPath
        /// </summary>
        Path,
        /// <summary>
        /// Jiný, zásadně kreslí potomek
        /// </summary>
        User
    }
    /// <summary>
    /// Typ třídění položek do grafu
    /// </summary>
    public enum GraphItemSortType
    {
        SortByPosition,
        SortByValueAsc,
        SortByValueDesc
    }
    #endregion
}
